# PersistentFieldHarvester.java

    package org.codefilarete.stalactite.mapping;

    import java.lang.reflect.Field;
    import java.lang.reflect.Modifier;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.function.Predicate;
    import java.util.stream.Collectors;

    import org.codefilarete.reflection.Accessors;
    import org.codefilarete.reflection.ReadWritePropertyAccessPoint;
    import org.codefilarete.reflection.ValueAccessPointMap;
    import org.codefilarete.stalactite.sql.ddl.structure.Column;
    import org.codefilarete.stalactite.sql.ddl.structure.Table;
    import org.codefilarete.tool.bean.FieldIterator;
    import org.codefilarete.tool.collection.Iterables;

    /**
     * @author Guillaume Mary
     */
    public class PersistentFieldHarvester {

    	private Map<ReadWritePropertyAccessPoint, Column> fieldToColumn;

    	private Map<String, Field> nameTofield;

    	public List<Field> getFields(Class clazz) {
    		FieldFilter fieldVisitor = getFieldVisitor();
    		return Iterables.stream(new FieldIterator(clazz)).filter(fieldVisitor).collect(Collectors.toList());
    	}

    	public Map<ReadWritePropertyAccessPoint, Column> getFieldToColumn() {
    		return fieldToColumn;
    	}

    	public <C, T extends Table<T>> Map<ReadWritePropertyAccessPoint<C, ?>, Column<T, ?>> mapFields(Class<C> clazz, T targetTable) {
    		List<Field> fields = getFields(clazz);
    		Map<String, Column<T, ?>> mapColumnsOnName = targetTable.mapColumnsOnName();
    		fieldToColumn = new ValueAccessPointMap<>();
    		nameTofield = new HashMap<>(5);
    		for (Field field : fields) {
    			Column<T, ?> column = mapColumnsOnName.get(field.getName());
    			if (column == null) {
    				column = newColumn(targetTable, field);
    			} // else column already exists, skip it to avoid duplicate column (even if type is different)
    			fieldToColumn.put(Accessors.propertyAccessor(field), column);
    			nameTofield.put(field.getName(), field);
    		}
    		return (Map) fieldToColumn;
    	}

    	protected <T extends Table> Column<T, Object> newColumn(T targetTable, Field field) {
    		return newColumn(targetTable, buildColumnName(field), field.getType());
    	}

    	protected <T extends Table> Column<T, Object> newColumn(T targetTable, String fieldName, Class type) {
    		return targetTable.addColumn(fieldName, type);
    	}

    	protected String buildColumnName(Field field) {
    		return field.getName();
    	}

    	public Field getField(String name) {
    		return nameTofield.get(name);
    	}

    	public Column getColumn(ReadWritePropertyAccessPoint field) {
    		return fieldToColumn.get(field);
    	}

    	/**
    	 * To be overridden to exclude unecessary Fields. Current implementation excludes static, Iterable and Map fields.
    	 * @return a {@link FieldFilter}
    	 */
    	protected FieldFilter getFieldVisitor() {
    		return new FieldFilter();
    	}

    	/**
    	 * Simple class that doesn't accept static fields
    	 */
    	public static class FieldFilter implements Predicate<Field> {
    		@Override
    		public boolean test(Field field) {
    			return !Modifier.isStatic(field.getModifiers());
    		}
    	}
    }